package org.codeanalyzer;

import org.codeanalyzer.model.ApiLibrary;

import com.sun.source.tree.ClassTree;
import com.sun.source.tree.MethodTree;
import com.sun.source.util.TreePath;
import com.sun.source.util.TreePathScanner;
import com.sun.source.util.Trees;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.JCArrayTypeTree;
import com.sun.tools.javac.tree.JCTree.JCClassDecl;
import com.sun.tools.javac.tree.JCTree.JCIdent;
import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
import com.sun.tools.javac.util.List;

public class ApiAnalyzerTreeVisitor extends TreePathScanner<Object, Trees>
{

    private final ApiLibrary apiLibrary;

    public ApiAnalyzerTreeVisitor(ApiLibrary apiLibrary)
    {
        this.apiLibrary = apiLibrary;
    }

    @Override
    public Object visitClass(ClassTree classTree, Trees trees)
    {
        System.out.print("\nVisiting " + classTree.getSimpleName());

        JCClassDecl classDecl = (JCClassDecl)classTree;
        String modifiers = classDecl.getModifiers().toString().trim();
        String packageName = getPackageName();

        apiLibrary.addClass(packageName, classTree.getSimpleName().toString(), modifiers);

        return super.visitClass(classTree, trees);
    }

    @Override
    public Object visitMethod(MethodTree methodTree, Trees trees)
    {
        System.out.print("\nVisiting " + methodTree.getName());

        JCClassDecl classDecl = getClassDecl();
        String classModifiers = classDecl.getModifiers().toString().trim();
        JCMethodDecl methodDecl = (JCMethodDecl)methodTree;
        String modifiers = methodDecl.getModifiers().toString().trim();
        String packageName = getPackageName();
        String simpleClassName = classDecl.getSimpleName().toString();
        String methodName = methodDecl.getName().toString();
        if (methodName.equals("<init>"))
            methodName = simpleClassName;

        String returnType = methodDecl.getReturnType() != null ? methodDecl.getReturnType().toString() : "";
        StringBuilder parameters = new StringBuilder();
        List<JCVariableDecl> jcParameters = methodDecl.getParameters();
        for(JCVariableDecl jcVariableDecl : jcParameters)
        {
            String parameter = "";
            if (jcVariableDecl.getType() instanceof JCArrayTypeTree)
            {
                JCArrayTypeTree type = (JCArrayTypeTree)jcVariableDecl.getType();
                parameter = type.type != null ? type.type.toString() : "";
            }
            else if (jcVariableDecl.getType() instanceof JCIdent)
            {
                JCIdent type = (JCIdent)jcVariableDecl.getType();
                parameter = type.type != null ? type.type.toString() : "";
            }
            else
            {
                JCTree type = (JCTree)jcVariableDecl.getType();
                parameter = type.type != null ? type.type.toString() : "";
            }
            parameters.append(parameters.length() == 0 ? "" : ",");
            parameters.append(parameter);
        }

        apiLibrary.addMethod(packageName, simpleClassName, classModifiers, methodName, modifiers, returnType,
            parameters.toString());

        return super.visitMethod(methodTree, trees);
    }

    // **************************************************************************************
    // Support methods
    // **************************************************************************************

    private String getPackageName()
    {
        TreePath path = getCurrentPath();
        return path.getCompilationUnit().getPackageName().toString();
    }

    private JCClassDecl getClassDecl()
    {
        TreePath parentPath = getCurrentPath().getParentPath();
        if (parentPath.getLeaf() instanceof JCClassDecl)
        {
            return (JCClassDecl)parentPath.getLeaf();
        }
        return null;
    }
}
